﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.Configuration;
using System.Text;
using System.Configuration;
using posttracker.Classes.Interfaces;
namespace posttracker.Classes
{                               
    public class WebApiMembershipProvider : MembershipProvider
    {
        private string pApplicationName;
        private bool pEnablePasswordReset;
        private bool pEnablePasswordRetrieval;
        private bool pRequiresQuestionAndAnswer;
        private bool pRequiresUniqueEmail;
        private int pMaxInvalidPasswordAttempts;
        private int pPasswordAttemptWindow;
        private MembershipPasswordFormat pPasswordFormat;
        private string pPasswordStrengthRegularExpression;
        private int pMinRequiredNonAlphanumericCharacters;
        private int pMinRequiredPasswordLength;

        private IRegistration reg;

        public override Boolean EnablePasswordReset { get { return pEnablePasswordReset; } }
        public override Boolean EnablePasswordRetrieval { get { return pEnablePasswordRetrieval; } }
        public override Boolean RequiresQuestionAndAnswer { get { return pRequiresQuestionAndAnswer; } }
        public override Boolean RequiresUniqueEmail { get { return pRequiresUniqueEmail; } }
        public override MembershipPasswordFormat PasswordFormat { get { return pPasswordFormat; } }
        public override int MaxInvalidPasswordAttempts { get { return pMaxInvalidPasswordAttempts; } }
        public override int PasswordAttemptWindow { get { return pPasswordAttemptWindow; } }
        public override string ApplicationName { set { pApplicationName = value; } get { return pApplicationName; } }
        public override int MinRequiredNonAlphanumericCharacters { get { return pMinRequiredNonAlphanumericCharacters; } }
        public override int MinRequiredPasswordLength { get { return pMinRequiredPasswordLength; } }
        public override string PasswordStrengthRegularExpression { get { return pPasswordStrengthRegularExpression; } }

        public WebApiMembershipProvider()
        {
            reg = new Registration();
            pEnablePasswordReset = true;
            /*MembershipSection membershipSection =
                WebConfigurationManager.GetSection("membership")
                as MembershipSection;
            membershipSection.
            Initialize("WebApiMembershipProvider", membershipSection[""]);*/


        }

        public void Initialize(string name, NameValueCollection config)
        {


            if (config == null)
                throw new ArgumentNullException("config");

            if (name == null || name.Length == 0)
                name = "WebApiMembershipProvider";

            if (String.IsNullOrEmpty(config["description"]))
            {
                config.Remove("description");
                config.Add("description", "API Membership provider");
            }
            //TODO uncomment
            // Initialize the abstract base class.
            //base.Initialize(name, config);

            pApplicationName = GetConfigValue(config["applicationName"],
                                            System.Web.Hosting.HostingEnvironment.ApplicationVirtualPath);
            pMaxInvalidPasswordAttempts = Convert.ToInt32(GetConfigValue(config["maxInvalidPasswordAttempts"], "5"));
            pPasswordAttemptWindow = Convert.ToInt32(GetConfigValue(config["passwordAttemptWindow"], "10"));
            pEnablePasswordReset = Convert.ToBoolean(GetConfigValue(config["enablePasswordReset"], "true"));
            pEnablePasswordRetrieval = Convert.ToBoolean(GetConfigValue(config["enablePasswordRetrieval"], "true"));
            pRequiresQuestionAndAnswer = Convert.ToBoolean(GetConfigValue(config["requiresQuestionAndAnswer"], "false"));
            pRequiresUniqueEmail = Convert.ToBoolean(GetConfigValue(config["requiresUniqueEmail"], "true"));
            //pWriteExceptionsToEventLog = Convert.ToBoolean(GetConfigValue(config["writeExceptionsToEventLog"], "true"));
            //pMinRequiredNonAlphanumericCharacters = Convert.ToInt32(GetConfigValue(config["minRequiredNonAlphanumericCharacters"], "1"));
            //pMinRequiredPasswordLength = Convert.ToInt32(GetConfigValue(config["minRequiredPasswordLength"], "7"));
            //pPasswordStrengthRegularExpression = Convert.ToString(GetConfigValue(config["passwordStrengthRegularExpression"], ""));
            string temp_format = config["passwordFormat"];
            if (temp_format == null)
            {
                temp_format = "Hashed";
            }

            switch (temp_format)
            {
                case "Hashed":
                    pPasswordFormat = MembershipPasswordFormat.Hashed;
                    break;
                case "Encrypted":
                    pPasswordFormat = MembershipPasswordFormat.Encrypted;
                    break;
                case "Clear":
                    pPasswordFormat = MembershipPasswordFormat.Clear;
                    break;
                default:
                    throw new System.Configuration.Provider.ProviderException("Password format not supported.");
            }

            //
            // Initialize OdbcConnection.
            //

           /* ConnectionStringSettings ConnectionStringSettings =
              ConfigurationManager.ConnectionStrings[config["connectionStringName"]];

            if (ConnectionStringSettings == null || ConnectionStringSettings.ConnectionString.Trim() == "")
            {
                throw new ProviderException("Connection string cannot be blank.");
            }

            connectionString = ConnectionStringSettings.ConnectionString;*/


            // Get encryption and decryption key information from the configuration.
            /*Configuration cfg =
              WebConfigurationManager.OpenWebConfiguration(System.Web.Hosting.HostingEnvironment.ApplicationVirtualPath);
            machineKey = (MachineKeySection)cfg.GetSection("system.web/machineKey");

            if (machineKey.ValidationKey.Contains("AutoGenerate"))
                if (PasswordFormat != MembershipPasswordFormat.Clear)
                    throw new System.Configuration.Provider.ProviderException("Hashed or Encrypted passwords " +
                                                "are not supported with auto-generated keys.");*/

        }

        private string GetConfigValue(string configValue, string defaultValue)
        {
            if (String.IsNullOrEmpty(configValue))
                return defaultValue;

            return configValue;
        }
        //TODO uncomment
        public override MembershipUser CreateUser(string name, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus createStatus)
        {
            //check on diplicate email
            if (!string.IsNullOrEmpty(GetUserNameByEmail(email)))
            {
                createStatus = MembershipCreateStatus.DuplicateEmail;
                return null;
            }

            if (GetUser(name, false) != null)
            {
                createStatus = MembershipCreateStatus.DuplicateUserName;
                return null;
            }
            //get hached password
            password = EncryptPassword(password);
            //membership user instance
            User user = new User(){ Password = password, Email = email };
            user.SetUserName(name);
           
            createStatus = reg.Register(ref user);
           

            return user;
        }

        public string EncryptPassword(string password)
        {
            //String to Byte Array
            byte[] passwordBytes = System.Text.Encoding.Unicode.GetBytes(password);
            //Encrypt password
            byte[] passwordBytesEncrypted = EncryptPassword(passwordBytes);
            //Byte Array to String 
            var passwordEncrypted = System.Text.Encoding.Unicode.GetString(passwordBytesEncrypted);
            return passwordEncrypted;
        }

        private static string Encrypt(string plaintextValue)
        {
            var plaintextBytes = Encoding.Unicode.GetBytes(plaintextValue);
            String[] str = new String[]{"Private message", "Recipient: API"};
            return Encoding.Unicode.GetString(MachineKey.Protect(plaintextBytes, str));
        }

        private static string Decrypt(string encryptedValue)
        {
            try
            {
                String[] str = new String[]{"Private message", "Recipient: API"};
                var decryptedBytes = MachineKey.Unprotect(Encoding.Unicode.GetBytes(encryptedValue), str);
                return Encoding.Unicode.GetString(decryptedBytes);
            }
            catch
            {
                return null;
            }
        }
       
        public override void UpdateUser(MembershipUser user)
        {
            
            reg.Update((User)user);
        }
       
        public override bool DeleteUser(string name, bool deleteAllRelatedData)
        {
            bool result = reg.DeleteUser(name, deleteAllRelatedData);
            return result;
        }
      
        public override bool ValidateUser(string name, string password)
        {
            bool result = reg.ValidateUser(name, EncryptPassword(password));
            return result;
        }
       
        public override MembershipUser GetUser(object providerUserKey, bool updateLastActivityDate)
        {
            User user = null;
            int res;
            if(int.TryParse(providerUserKey.ToString(), out res))
            {
                user = reg.GetUser(res, updateLastActivityDate);
            }
            return user;
            
        }
       
        public override MembershipUser GetUser(string name, bool updateLastActivityDate)
        {
            User user = reg.GetUser(name, updateLastActivityDate);
            if (user == null)
                return null;
            return user;

        }
  
        public override MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords)
        {
            MembershipUserCollection users = reg.GetAllUsers(pageIndex, pageSize, out totalRecords);
            if (users == null)
                return new MembershipUserCollection();
            return users;
            
        }
        public override string GetUserNameByEmail(string Email)
        {
            
            User user = reg.GetUserByEmail(Email);
            if (user == null)
                return string.Empty;
            return user.UserName;
        }

        public override int GetNumberOfUsersOnline()
        {
            TimeSpan onlineSpan = new TimeSpan(0,System.Web.Security.Membership.UserIsOnlineTimeWindow,0);
            DateTime timeDif = DateTime.Now.Subtract(onlineSpan);
            int numberOnlineUsers = reg.GetNumberOfUsersOnline(timeDif);
            return numberOnlineUsers;
        }
        public override string ResetPassword(string name, string answer)
        {
           
            if (System.Web.Security.Membership.EnablePasswordReset)
            {
                string newPassword = System.Web.Security.Membership.GeneratePassword(6, 2);

                ValidatePasswordEventArgs args =
                 new ValidatePasswordEventArgs(name, newPassword, false);

                OnValidatingPassword(args);

                if (args.Cancel)
                {
                    throw new System.Configuration.Provider.ProviderException("Generated password incorrect.");
                }
                string oldPassword = GetPassword(name, "");
                if (ChangePassword(name, oldPassword, EncryptPassword(newPassword)))
                {
                    return newPassword;
                }
                else
                {
                    return string.Empty;
                }

            }

            throw new NotSupportedException();
        }

        public override bool ChangePassword(string name, string oldPassword, string newPassword)
        {
            return reg.ChangePassword(name, oldPassword, newPassword);

        }
        
        public override string GetPassword(string name, string answer)
        {
            if (System.Web.Security.Membership.EnablePasswordRetrieval)
            {
                if (RequiresQuestionAndAnswer)
                    throw new NotImplementedException();

                return GetPassword(name);
            }

            throw new System.Configuration.Provider.ProviderException("Get password disabled.");
        }
        //not tested
        private string GetPassword(string name)
        {
            return reg.GetPassword(name);
        }

        public override bool ChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion, string newPasswordAnswer)
        {
            throw new NotImplementedException();

            return false;
        }


        public override MembershipUserCollection FindUsersByName(string usernameToMatch, int pageIndex, int pageSize, out int totalRecords)
        {
            MembershipUserCollection users = reg.FindUsersByName(usernameToMatch, pageIndex, pageSize, out totalRecords);
            if (users == null)
                return new MembershipUserCollection();
            return users;

        }

        public override MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, out int totalRecords)
        {
            MembershipUserCollection users = reg.FindUsersByEmail(emailToMatch, pageIndex, pageSize, out totalRecords);
            if (users == null)
                return new MembershipUserCollection();
            return users;

        }


        public override bool UnlockUser(string name)
        {
            return reg.UnlockUser(name);
        }


        protected override void OnValidatingPassword(ValidatePasswordEventArgs e)
        {
            if (e.Password.Length < 6)
            {
                e.Cancel = true;
            }
            base.OnValidatingPassword(e);
        }

        protected override byte[] EncryptPassword(byte[] password)
        {
            var sha = new System.Security.Cryptography.SHA512Managed();
            return  sha.ComputeHash(password);
            
        }

    }
}